﻿using Aeon.Emulator.Sound.FM.Operators;

namespace Aeon.Emulator.Sound.FM.Channels
{
    /// <summary>
    /// Emulates a 2-operator OPL channel.
    /// </summary>
    internal class Channel2 : Channel
    {
        protected readonly StandardOperator operator1;
        protected readonly StandardOperator operator2;

        private double previousFeedback;
        private double currentFeedback;

        /// <summary>
        /// Initializes a new instance of the <see cref="Channel2"/> class.
        /// </summary>
        /// <param name="baseAddress">The base address of the channel's control registers.</param>
        /// <param name="operator1">The first operator in the channel.</param>
        /// <param name="operator2">The second operator in the channel.</param>
        /// <param name="opl">The synthesizer which owns the channel.</param>
        public Channel2(int baseAddress, StandardOperator operator1, StandardOperator operator2, FmSynthesizer opl)
            : base(baseAddress, opl)
        {
            this.operator1 = operator1;
            this.operator2 = operator2;
        }

        /// <summary>
        /// Returns an array containing the channel's output values.
        /// </summary>
        /// <returns>Array containing the channel's output values.</returns>
        protected override double GetChannelOutput()
        {
            double channelOutput = 0;
            double op1Output;

            if(this.OperatorCount == 0)
            {
                if(operator2.AdsrState == AdsrState.Off)
                    return 0;
                op1Output = this.operator1.GetOperatorOutput((this.previousFeedback + this.currentFeedback) / 2.0);
                channelOutput = this.operator2.GetOperatorOutput(op1Output * 4);
            }
            else
            {
                if((this.operator1.AdsrState | this.operator2.AdsrState) == AdsrState.Off)
                    return 0;
                op1Output = this.operator1.GetOperatorOutput((this.previousFeedback + this.currentFeedback) / 2.0);
                var op2Output = this.operator2.GetOperatorOutput();
                channelOutput = (op1Output + op2Output) / 2.0;
            }

            this.previousFeedback = this.currentFeedback;
            this.currentFeedback = this.GetFeedbackValue(op1Output);

            return channelOutput;
        }
        /// <summary>
        /// Activates channel output.
        /// </summary>
        protected override void KeyOn()
        {
            this.operator1.KeyOn();
            this.operator2.KeyOn();
            this.previousFeedback = 0;
            this.currentFeedback = 0;
        }
        /// <summary>
        /// Disables channel output.
        /// </summary>
        protected override void KeyOff()
        {
            this.operator1.KeyOff();
            this.operator2.KeyOff();
        }
        /// <summary>
        /// Updates the state of all of the operators in the channel.
        /// </summary>
        protected override void UpdateOperators()
        {
            int keyScaleNumber = this.KeyScaleNumber;
            operator1.SetChannelData(this.FrequencyNumber, keyScaleNumber, this.Block);
            operator2.SetChannelData(this.FrequencyNumber, keyScaleNumber, this.Block);
        }
    }
}
